Skip to content

feat(tbor): implement RSA-AES key unwrap (GetUnwrappingKey + UnwrapKey) - #583

Merged
Vishal Soni (vsonims) merged 8 commits into
mainfrom
tbor/unwrap-key
Jul 23, 2026
Merged

feat(tbor): implement RSA-AES key unwrap (GetUnwrappingKey + UnwrapKey)#583
Vishal Soni (vsonims) merged 8 commits into
mainfrom
tbor/unwrap-key

Conversation

@vsonims

@vsonims Vishal Soni (vsonims) commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the TBOR key-import pair that lets a host RSA-AES-wrap a
key to a partition and re-import it as a masked (unmask-on-use) blob
— the TBOR analogue of MBOR RsaUnwrap, but re-masking the recovered
key under a scope's masking key instead of vaulting it under a key
handle.

Reordered stack: this now sits below the HMAC keygen+mac PR
(#580), so the RSA/AES/ECC key-import path is independent of the HMAC
command work. As the first general crypto command in the stack, it
also introduces the shared session/masking helpers and the shared
HashAlgo wire enum (relocated into key_props). (Replaces the
auto-merged #581, which was closed as a side effect of the branch
reorder.)

GetUnwrappingKey (opcode 0x13)

Returns the partition's RSA-2048 unwrapping public key in HSM wire
format (n_le(256) ‖ e_le(4)). The private half never leaves the
device; UnwrapKey resolves it internally by the partition's
RSA_UNWRAPPING_KEY_ID property. Materialised lazily (std/emulator PAL)
or generated in the background from partition init (hardware); an absent
key surfaces as PendingKeyGeneration so the host retries. Both CO/CU.

UnwrapKey (opcode 0x14)

Within an open CO/CU session, unwrap a host-supplied
RSA-OAEP(KEK) ‖ AES-KWP(key) blob with the partition unwrapping key and
return the recovered key masked (AEAD-GCM-256) under the requested
scope's masking key. Supports the AES, RSA (plain/CRT), ECC, and HMAC
key classes via the shared azihsm_fw_hsm_key_unwrap /
azihsm_fw_hsm_key_decode crates; the decode crate gains an HMAC path
(host-selected SHA variant → the variable-length VarLenHmacShaN kinds). The re-derived wire public key is returned
for the asymmetric (RSA/ECC) classes; symmetric classes return an empty
pub_key. Persists nothing. Both CO/CU.

Shared helpers introduced here

Tests / validation

  • Emu round-trips: GetUnwrappingKey; UnwrapKey AES (masked, no
    pubkey) and HMAC key-class (recovered kind = VarLenHmacSha256). The full
    unwrap→MAC round-trip lives in feat(tbor): implement HmacGenerateKey + Hmac (masked-key HMAC crypto) #580, which builds on this command.
  • Full TBOR emu suite green (114 tests at this layer); clippy clean;
    nightly fmt + copyright clean; Uno (thumbv7em) build passes.
  • Docs: docs/tbor-ddi/commands/{get_unwrapping_key,unwrap_key}.md +
    README rows (0x13, 0x14).

Endianness note (host wrap)

GetUnwrappingKey returns the modulus/exponent little-endian, and the
device expects the OAEP ciphertext in wire-LE (it flips to big-endian
internally for OpenSSL). The emu test's host-side wrap reverses the
pubkey components (LE→BE for from_hsm_bytes) and the RSA-OAEP
ciphertext (BE→LE) accordingly.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds the TBOR RSA‑AES key import command pair (GetUnwrappingKey / UnwrapKey) to allow a host to wrap key material to a partition and re-import it as a masked (unmask-on-use) blob, plus supporting plumbing (shared session/masking helpers, HashAlgo wire enum) and tests/docs. It also updates the SessionEx schedule to use a 32‑byte AES‑GCM masking key (TBOR) while keeping legacy MBOR’s 80‑byte CBC masking key schedule.

Changes:

  • Introduces TBOR opcodes 0x13 (GetUnwrappingKey) and 0x14 (UnwrapKey) end-to-end: wire types, dispatcher routing, handlers, host wrappers, and docs.
  • Adds shared TBOR helper(s) for active-session validation and resolving per-scope masking keys; moves/introduces HashAlgo in shared key_props.
  • Extends key decode to support HMAC key material (32/48/64 B → _HmacSha*) and adds TBOR derive support for “reserve + fill later” for mutable buffer fields.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
fw/plat/uno/fw/pal/src/session.rs Updates SessionEx blob sizing/validation to use 32-byte masking key for TBOR.
fw/plat/std/pal/src/session.rs Updates SessionEx blob sizing and makes session masking-key extraction length-aware (legacy vs SessionEx).
fw/pal/traits/src/lib.rs Changes SESSION_MASKING_KEY_LEN to 32 and clarifies it’s for TBOR SessionEx masking.
fw/core/lib/src/op.rs Classifies new TBOR opcodes as InSession for session-flag validation.
fw/core/lib/src/ddi/tbor/unwrap_key.rs New TBOR UnwrapKey handler: unwrap, decode, scope-mask, and respond (incl. pubkey for RSA/ECC).
fw/core/lib/src/ddi/tbor/session_open_finish.rs Updates SessionEx schedule documentation to 32-byte AES-GCM masking key.
fw/core/lib/src/ddi/tbor/mod.rs Adds opcode constants, dispatch routing, active-session validation helper, and masking-key resolver helper.
fw/core/lib/src/ddi/tbor/get_unwrapping_key.rs New TBOR GetUnwrappingKey handler: derives and returns partition RSA unwrapping public key.
fw/core/lib/src/ddi/mod.rs Adds a shared hmac_hash helper at the command level for both codecs.
fw/core/lib/src/ddi/mbor/hmac.rs Switches to shared crate::ddi::hmac_hash.
fw/core/lib/src/ddi/mbor/from_pal.rs Removes the duplicated MBOR-only hmac_hash mapping helper.
fw/core/key-decode/src/lib.rs Adds HMAC as a decode class and wires it into decode().
fw/core/key-decode/src/hmac.rs New HMAC decode path (length-based classification).
fw/core/ddi/tbor/types/src/unwrap_key.rs New TBOR wire schema for UnwrapKey.
fw/core/ddi/tbor/types/src/lib.rs Exposes new TBOR type modules (get_unwrapping_key, unwrap_key).
fw/core/ddi/tbor/types/src/key_props.rs Expands key-props to include shared HashAlgo wire enum.
fw/core/ddi/tbor/types/src/get_unwrapping_key.rs New TBOR wire schema for GetUnwrappingKey.
fw/core/ddi/tbor/derive/src/schema.rs Documents semantics of mutable fields and the reserve+fill pattern.
fw/core/ddi/tbor/derive/src/codegen_enc.rs Implements encoder generation for <field>_reserve(len) for mutable buffers.
docs/tbor-ddi/README.md Adds 0x13/0x14 to the TBOR command table.
docs/tbor-ddi/commands/unwrap_key.md New documentation page for UnwrapKey.
docs/tbor-ddi/commands/get_unwrapping_key.md New documentation page for GetUnwrappingKey.
ddi/tbor/types/tests/commands/unwrap_key.rs New emu integration tests for UnwrapKey (AES + HMAC).
ddi/tbor/types/tests/commands/mod.rs Registers new TBOR command tests.
ddi/tbor/types/tests/commands/get_unwrapping_key.rs New emu integration tests for GetUnwrappingKey (length/non-zero/stability).
ddi/tbor/types/src/unwrap_key.rs New host-side TBOR request/response wrappers for UnwrapKey.
ddi/tbor/types/src/lib.rs Re-exports new host-side command wrappers.
ddi/tbor/types/src/get_unwrapping_key.rs New host-side TBOR request/response wrappers for GetUnwrappingKey.

Comment thread fw/core/ddi/tbor/types/src/get_unwrapping_key.rs
Comment thread fw/core/ddi/tbor/types/src/unwrap_key.rs
Comment thread fw/core/lib/src/ddi/tbor/mod.rs
@vsonims

Copy link
Copy Markdown
Collaborator Author

Update: this PR now also introduces encoder "reserve + fill" support in the TBOR derive. For any #[tbor(mutable)] buffer / sealed_key field, the encoder emits a <field>_reserve(len) setter that reserves the data slot without copying, so a response can be built with reserved slots and filled in place via decode_mut (zero-copy — the TBOR analogue of MBOR's reserve/from_layout). GetUnwrappingKey now uses it: the PAL derives the wire public key straight into the reserved response slot (no scratch buffer, no copy). This infrastructure is reused by the stacked HMAC (#580) and AES key-generate commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (3)

fw/core/ddi/tbor/types/src/get_unwrapping_key.rs:9

  • The rustdoc link for UnwrapKey points to crate::hmac, which is unrelated and breaks navigation for readers of the wire schema docs.
//! `GetUnwrappingKey` is an in-session command that returns the
//! partition's RSA-2048 **unwrapping** public key, which the host uses to
//! RSA-AES key-wrap a payload for [`UnwrapKey`](crate::hmac).  The
//! unwrapping key is a device-provisioned partition-internal key; only its

fw/core/ddi/tbor/types/src/unwrap_key.rs:59

  • The constants are described as being "pinned" into #[tbor(... max_len = ...)] literals, but there are no compile-time assertions to prevent them from drifting out of sync (a pattern used in other TBOR type schemas).
pub const UNWRAP_PUB_KEY_MAX_LEN: usize = 520;

fw/core/lib/src/ddi/tbor/mod.rs:229

  • resolve_masking_key uses pal.session_masking_key() for KeyScope::Session, but the Uno PAL currently returns UnsupportedCmd from session_masking_key (fw/plat/uno/fw/pal/src/session.rs:333-335). This makes Session-scoped masking fail on Uno/hardware even though the docs and handler comments indicate it should work for any Active session.
) -> HsmResult<&'p DmaBuf> {
    match scope {
        HsmKeyScope::Session => pal.session_masking_key(io, sess_id),
        _ => {
            let mk_key_id = masking_key_id_for_scope(pal, io, scope)?;
            pal.vault_key(io, mk_key_id)
        }

Comment thread fw/core/ddi/tbor/derive/src/codegen_enc.rs
Comment thread docs/tbor-ddi/commands/unwrap_key.md
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 19, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Copilot AI review requested due to automatic review settings July 19, 2026 03:32
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 19, 2026
Add the TBOR RsaModExp command (opcode 0x1A): the RSA private-key
primitive x = y^d mod n using a caller-held masked RSA private key
(imported via UnwrapKey with the RSA / RSA-CRT key class). The device
unmasks the key in place (recovering its modulus size and CRT form from
the blob's key kind), checks the op's usage attribute (Decrypt -> decrypt,
Sign -> sign), computes the modular exponentiation into the reserved
response slot, and scrubs the recovered key on every path. The raw
primitive underlying RSA decrypt / sign — the host applies and removes any
padding. TBOR analogue of MBOR RsaModExp; there is no TBOR RSA key
generation, so RSA keys enter only through UnwrapKey.

Wire schemas (fw + host mirror) with a 1-byte RsaOp selector; dispatch /
classifier / op.rs wiring; a strict from_pal::rsa_key mapping (rejects
non-RSA-private kinds as InvalidKeyType). Emu tests cover host-verified
RSA sign/decrypt roundtrips for 2K/3K/4K and 4K-CRT through the full
UnwrapKey(Rsa) -> RsaModExp path, plus wrong-y-length and wrong-key-class
rejects. Per-command docs added.

Based on tbor/unwrap-key (#583), which provides the DMA-budget-efficient
UnwrapKey that lets RSA-3072/4096 keys be imported within 8 KB.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 19, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (3)

fw/core/ddi/tbor/types/src/get_unwrapping_key.rs:9

  • Rustdoc link target is incorrect: crate::hmac doesn’t exist in this module and will produce a broken intra-doc link. This should point to the unwrap_key schema/module.
//! `GetUnwrappingKey` is an in-session command that returns the
//! partition's RSA-2048 **unwrapping** public key, which the host uses to
//! RSA-AES key-wrap a payload for [`UnwrapKey`](crate::hmac).  The
//! unwrapping key is a device-provisioned partition-internal key; only its

fw/core/ddi/tbor/types/src/unwrap_key.rs:59

  • The max-len constants are described as “pinned” to #[tbor(... max_len = ...)] literals, but there are no const _: () = assert!(...) tripwires to keep them in sync. Other TBOR schemas use these asserts to prevent silent drift (e.g. get_unwrapping_key.rs).
/// Max recovered public-key length (RSA-4096 `n_le(512) ‖ e_le(4)`).
/// Pinned into the `#[tbor(buffer, max_len = 520)]` literal on
/// [`TborUnwrapKeyResp::pub_key`].
pub const UNWRAP_PUB_KEY_MAX_LEN: usize = 520;

fw/core/lib/src/ddi/tbor/mod.rs:239

  • resolve_masking_key assumes HsmKeyScope::Session can always be satisfied via pal.session_masking_key(...), but on the Uno PAL this method currently returns UnsupportedCmd (see fw/plat/uno/fw/pal/src/session.rs:333-335). This means Session-scoped masking (documented as supported) will fail on hardware unless the Uno PAL implements session_masking_key for SessionEx blobs (32-byte AES-GCM key).
/// Resolve the 32-byte AES-256-GCM masking key material for `scope`,
/// returning a borrow of the on-device key.
///
/// `Session` scope resolves to the per-session masking key
/// ([`HsmSessionManager::session_masking_key`](azihsm_fw_hsm_pal_traits::HsmSessionManager::session_masking_key));
/// every other scope resolves via [`masking_key_id_for_scope`] and reads
/// the vault key.  All scopes yield a 32-byte key suitable for the
/// `key_masking::aead` (AES-GCM-256) masked-key system.
fn resolve_masking_key<'p, P: HsmPal>(
    pal: &'p P,
    io: &impl HsmIo,
    scope: HsmKeyScope,
    sess_id: HsmSessId,
) -> HsmResult<&'p DmaBuf> {
    match scope {
        HsmKeyScope::Session => pal.session_masking_key(io, sess_id),
        _ => {
            let mk_key_id = masking_key_id_for_scope(pal, io, scope)?;
            pal.vault_key(io, mk_key_id)
        }
    }

Comment thread fw/core/lib/src/ddi/tbor/unwrap_key.rs
Comment thread fw/core/lib/src/ddi/tbor/unwrap_key.rs Outdated
Copilot AI review requested due to automatic review settings July 20, 2026 19:23
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 20, 2026
Add the TBOR RsaModExp command (opcode 0x1A): the RSA private-key
primitive x = y^d mod n using a caller-held masked RSA private key
(imported via UnwrapKey with the RSA / RSA-CRT key class). The device
unmasks the key in place (recovering its modulus size and CRT form from
the blob's key kind), checks the op's usage attribute (Decrypt -> decrypt,
Sign -> sign), computes the modular exponentiation into the reserved
response slot, and scrubs the recovered key on every path. The raw
primitive underlying RSA decrypt / sign — the host applies and removes any
padding. TBOR analogue of MBOR RsaModExp; there is no TBOR RSA key
generation, so RSA keys enter only through UnwrapKey.

Wire schemas (fw + host mirror) with a 1-byte RsaOp selector; dispatch /
classifier / op.rs wiring; a strict from_pal::rsa_key mapping (rejects
non-RSA-private kinds as InvalidKeyType). Emu tests cover host-verified
RSA sign/decrypt roundtrips for 2K/3K/4K and 4K-CRT through the full
UnwrapKey(Rsa) -> RsaModExp path, plus wrong-y-length and wrong-key-class
rejects. Per-command docs added.

Based on tbor/unwrap-key (#583), which provides the DMA-budget-efficient
UnwrapKey that lets RSA-3072/4096 keys be imported within 8 KB.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 20, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

fw/pal/traits/src/lib.rs:440

  • The doc for SESSION_MASKING_KEY_LEN says the legacy MBOR Session masking key is “sized by the std-PAL-local SESSION_MASKING_KEY_SIZE”, but the same local constant exists in other PALs too (e.g. Uno). This wording is misleading for implementers/readers trying to locate the legacy sizing constant.
/// Present for both CO and CU SessionEx blobs.  Distinct from the legacy
/// MBOR `Session`-blob masking key (an 80 B `aes32 ‖ hmac48` cbc key,
/// sized by the std-PAL-local `SESSION_MASKING_KEY_SIZE`), which the
/// `key_masking::cbc`-based MBOR masked-key system continues to use.

Comment thread fw/core/lib/src/ddi/tbor/mod.rs
…ey len)

- Rename the `oaep_hash` request field to `oaep_hash_algo` (fw + host wire
  types, handler, tests, docs) for clarity, per review.
- `resolve_masking_key` now validates that a `Session`-scope masking key is
  exactly SESSION_MASKING_KEY_LEN (32) bytes, rejecting a legacy 80-byte
  MBOR `Session` blob key with `UnsupportedKeyScope` so a TBOR masked-key
  command can't accidentally run under a legacy session schedule.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6cd9bfef-fd58-4ac9-bbb0-eb67febe865d
Copilot AI review requested due to automatic review settings July 21, 2026 00:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

fw/pal/traits/src/lib.rs:436

  • SESSION_MASKING_KEY_LEN is updated to 32 bytes for TBOR SessionEx, but the HsmVaultKeyKind::SessionEx documentation still describes the SessionEx blob as carrying masking_key(80) with sizes 120/216 (see fw/pal/traits/src/vault.rs:160-164). Please update that enum doc (and any other related docs) to reflect the new masking_key(32) layout (72/168) so implementers don’t rely on the old sizing.
/// Length in bytes of the per-session `masking_key` for TBOR
/// **SessionEx (CU/CO)** sessions.
///
/// 32 B AES-256-GCM key used by the `key_masking::aead`-based TBOR
/// masked-key system (masked keys are scoped and wrapped under AES-GCM).

Comment thread fw/core/key-decode/src/lib.rs
Address review feedback (jaygmsft): the handler hardcoded both permissions
per key class (e.g. RSA always got sign+decrypt), deviating from the MBOR
import path where the caller selects the usage.

Add a compact 1-byte `KeyUsage` bitfield to the shared TBOR `key_props`
(encrypt/decrypt/sign/verify/derive/wrap/unwrap) and carry it on the
`UnwrapKey` request. The handler now derives the recovered key's vault
attributes from the requested usage, mirroring the MBOR `key_attrs`
policy: sign+verify and encrypt+decrypt are matched pairs, exactly one
usage group may be set, and each class restricts the valid group(s)
(AES: enc/dec; RSA: sign/verify or enc/dec; ECC: sign/verify or derive;
HMAC: sign/verify). Invalid combinations are rejected with
InvalidPermissions.

Adds host KEY_USAGE_* constants, an emu rejection test, and doc updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6cd9bfef-fd58-4ac9-bbb0-eb67febe865d
Copilot AI review requested due to automatic review settings July 21, 2026 00:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated no new comments.

Address review feedback: the SessionEx masking key is now 32 B
(SESSION_MASKING_KEY_LEN, AES-256-GCM), so the SessionEx blob is 72 B
(PlainText/CU) / 168 B (Authenticated/CO), not the old 120/216 B with an
80 B masking key. Update the stale `HsmVaultKeyKind::SessionEx` and
`session_promote` doc comments accordingly; the legacy MBOR `Session`
blob's 80 B CBC key is called out as distinct.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6cd9bfef-fd58-4ac9-bbb0-eb67febe865d
Copilot AI review requested due to automatic review settings July 22, 2026 21:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 3 comments.

Comment thread ddi/tbor/types/tests/commands/unwrap_key.rs
Comment thread docs/tbor-ddi/commands/unwrap_key.md Outdated
Comment thread docs/tbor-ddi/commands/unwrap_key.md
…inal; add ECC unwrap test

Address review feedback:
- `masking_key_id_for_scope` now remaps `PartPropNotFound` to
  `UnsupportedKeyScope` for `Ephemeral`/`Local` scopes too (not just
  `SecurityDomain`), so requesting a scope before its masking key is
  provisioned surfaces the single documented `UnsupportedKeyScope`
  instead of leaking `PartPropNotFound`. Docs corrected accordingly
  (the prior `InvalidArg`-before-`Initialized` claim was inaccurate).
- Add an emu `UnwrapKey` round-trip for the ECC (P-256) key class,
  exercising the PKCS#8 decode + re-derived `x‖y` public-key path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6cd9bfef-fd58-4ac9-bbb0-eb67febe865d
Copilot AI review requested due to automatic review settings July 22, 2026 22:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated no new comments.

Comment thread ddi/tbor/types/src/unwrap_key.rs
Comment thread fw/core/lib/src/ddi/tbor/unwrap_key.rs

@jaygmsft Jayant Gandhi (jaygmsft) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with suggestions

@vsonims
Vishal Soni (vsonims) added this pull request to the merge queue Jul 23, 2026
Merged via the queue into main with commit 594640a Jul 23, 2026
30 checks passed
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 29, 2026
Add the TBOR RsaModExp command (opcode 0x1A): the RSA private-key
primitive x = y^d mod n using a caller-held masked RSA private key
(imported via UnwrapKey with the RSA / RSA-CRT key class). The device
unmasks the key in place (recovering its modulus size and CRT form from
the blob's key kind), checks the op's usage attribute (Decrypt -> decrypt,
Sign -> sign), computes the modular exponentiation into the reserved
response slot, and scrubs the recovered key on every path. The raw
primitive underlying RSA decrypt / sign — the host applies and removes any
padding. TBOR analogue of MBOR RsaModExp; there is no TBOR RSA key
generation, so RSA keys enter only through UnwrapKey.

Wire schemas (fw + host mirror) with a 1-byte RsaOp selector; dispatch /
classifier / op.rs wiring; a strict from_pal::rsa_key mapping (rejects
non-RSA-private kinds as InvalidKeyType). Emu tests cover host-verified
RSA sign/decrypt roundtrips for 2K/3K/4K and 4K-CRT through the full
UnwrapKey(Rsa) -> RsaModExp path, plus wrong-y-length and wrong-key-class
rejects. Per-command docs added.

Based on tbor/unwrap-key (#583), which provides the DMA-budget-efficient
UnwrapKey that lets RSA-3072/4096 keys be imported within 8 KB.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Vishal Soni (vsonims) added a commit that referenced this pull request Jul 29, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Abhijit Nathwani (msft-nathwania) pushed a commit to msft-nathwania/azihsm-sdk that referenced this pull request Jul 31, 2026
Add the TBOR RsaModExp command (opcode 0x1A): the RSA private-key
primitive x = y^d mod n using a caller-held masked RSA private key
(imported via UnwrapKey with the RSA / RSA-CRT key class). The device
unmasks the key in place (recovering its modulus size and CRT form from
the blob's key kind), checks the op's usage attribute (Decrypt -> decrypt,
Sign -> sign), computes the modular exponentiation into the reserved
response slot, and scrubs the recovered key on every path. The raw
primitive underlying RSA decrypt / sign — the host applies and removes any
padding. TBOR analogue of MBOR RsaModExp; there is no TBOR RSA key
generation, so RSA keys enter only through UnwrapKey.

Wire schemas (fw + host mirror) with a 1-byte RsaOp selector; dispatch /
classifier / op.rs wiring; a strict from_pal::rsa_key mapping (rejects
non-RSA-private kinds as InvalidKeyType). Emu tests cover host-verified
RSA sign/decrypt roundtrips for 2K/3K/4K and 4K-CRT through the full
UnwrapKey(Rsa) -> RsaModExp path, plus wrong-y-length and wrong-key-class
rejects. Per-command docs added.

Based on tbor/unwrap-key (Azure#583), which provides the DMA-budget-efficient
UnwrapKey that lets RSA-3072/4096 keys be imported within 8 KB.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Rajesh Gali (rajesh-gali) pushed a commit that referenced this pull request Aug 3, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
@jaygmsft
Jayant Gandhi (jaygmsft) deleted the tbor/unwrap-key branch August 4, 2026 17:28
Rajesh Gali (rajesh-gali) pushed a commit that referenced this pull request Aug 4, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
SivaParvathip21 pushed a commit to SivaParvathip21/azihsm-sdk that referenced this pull request Aug 5, 2026
Add the TBOR Hash command (opcode 0x1B): within an open session,
compute a SHA-256 / 384 / 512 digest of a host-supplied message and
return it. A pure hashing utility — no key, no scope, no partition state
— the TBOR analogue of MBOR ShaDigest. Named generically (Hash rather
than ShaDigest) to leave room for future XOF / SHAKE variants.

Uses the reserve-then-fill pattern: the response frame is encoded with the
digest slot reserved, then the PAL hashes straight into it (natural
big-endian output) — no intermediate buffer, no copy. Reuses the shared
HashAlgo enum from key_props.

Wire schemas (fw + host mirror); dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated). Emu tests
verify device digests byte-for-byte against host-computed SHA
(azihsm_crypto) for all three algorithms over short / empty / long
messages, plus an unknown-algo reject. Per-command docs added.

Based on tbor/unwrap-key (Azure#583) for the shared HashAlgo enum; uses opcode
0x1B to avoid collision with the sibling AES / ECC / RSA crypto branches
(0x15..0x1A).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants